﻿// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
//
// App.xaml.cpp
// Implementation of the App class.
//

#include "pch.h"
#include "ExceptionPolicyFactory.h"
#include "DataModel\ForecastManager.h"
#include "LoadPage.xaml.h"
#include "LocationData.h"
#include "MainPage.xaml.h"
#include "SearchResultsPage.xaml.h"
#include "SettingsFlyout.xaml.h"
#include "Common\SuspensionManager.h"

using namespace Weathr;

using namespace concurrency;
using namespace Platform;
using namespace Platform::Collections;
using namespace std;
using namespace Windows::ApplicationModel;
using namespace Windows::ApplicationModel::Activation;
using namespace Windows::ApplicationModel::Search;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::Storage;
using namespace Windows::UI::ApplicationSettings;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Controls::Primitives;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Interop;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Navigation;


/// <summary>
/// Initializes the singleton application object.  This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
App::App() : m_exceptionPolicy(ExceptionPolicyFactory::GetCurrentPolicy())
{
#ifndef NDEBUG
    // Remember thread ID of main thread for assertion checking of runtime context.
    RecordMainThread();
#endif

    InitializeComponent();
    Suspending += ref new SuspendingEventHandler(this, &App::OnSuspend);
    Resuming += ref new EventHandler<Object^>(this, &App::OnResume);

    ForecastManager::Current = ref new ForecastManager();
}

/// <summary>
/// Invoked when the application is launched normally by the end user.  Other entry points
/// will be used when the application is launched to open a specific file, to display
/// search results, and so forth.
/// </summary>
/// <param name="e">Details about the launch request and process.</param>
void App::OnLaunched(LaunchActivatedEventArgs^ e)
{
    // If this is not the first activation (for example, if the app is already running and the user selects the
    // secondary tile), just navigate to the main page with the provided arguments.
    if (Window::Current->Content != nullptr)
    {
        ActivateLaunch(MainPage::typeid, e->Arguments, e, false);
        Window::Current->Activate();
        return;
    }

    // Otherwise, this is the first launch. Show the splash screen while we load up the app.

    auto navigatePredicate = [this, e]()
    {
        ActivateLaunch(MainPage::typeid, e->Arguments, e, false);
    };

    auto asyncLoadFunction = [this]
    {
        return LocationData::Current().LoadUserLocationsAsync().then([](task<web::json::value> previousTask)
        {
            assert(IsMainThread());
            try
            {
                ForecastManager::Current->LoadLocations(previousTask.get());
                return task_from_result();
            }
            catch (COMException^ e)
            {
                // File was not found. Just load the default locations.
                return LocationData::Current().LoadAppLocationsAsync().then([](task<web::json::value> previousTask)
                {
                    assert(IsMainThread());
                    try
                    {
                        ForecastManager::Current->LoadLocations(previousTask.get());
                    }
                    catch (COMException^ e)
                    {
                        // File was not found. Swallow the exception.
                        // TODO: Open question: what should be done at the point where the app can't initialize?
                    }
                }, task_continuation_context::use_current());
            }

        }, task_continuation_context::use_current()).then([](task<void> previousTask)
        {
            assert(IsMainThread());
            auto forecastManager = ForecastManager::Current;
            if (forecastManager->HomeForecast == nullptr)
            {
                forecastManager->HomeForecast = forecastManager->FindForecast(forecastManager->DefaultHomeForecastUid);
            }

            LonLatController::Current->SetOrientation(DirectX::XMConvertToRadians(forecastManager->HomeForecast->Latitude), DirectX::XMConvertToRadians(forecastManager->HomeForecast->Longitude));

        }, task_continuation_context::use_current()).then(ObserveException<void>(m_exceptionPolicy));
    };

    Window::Current->Content = ref new LoadPage(e->SplashScreen, navigatePredicate, asyncLoadFunction, m_exceptionPolicy);

    Window::Current->Activate();
}

/// <summary>
/// Invoked when application execution is being suspended.  Application state is saved
/// without knowing whether the application will be terminated or resumed with the contents
/// of memory still intact.
/// </summary>
/// <param name="sender">The source of the suspend request.</param>
/// <param name="e">Details about the suspend request.</param>
void App::OnSuspend(Object^ sender, SuspendingEventArgs^ e)
{
    (void) sender;	// Unused parameter

    auto deferral = e->SuspendingOperation->GetDeferral();

    // NOTE: We pass the app's local settings, which isn't consistent with SuspensionManager, but DirectXPage currently
    // doesn't use the value.
    m_directXPage->SaveInternalState(ApplicationData::Current->LocalSettings->Values);

    Common::SuspensionManager::SaveAsync().then([deferral](task<void> antecedent)
    {
        deferral->Complete();

    }).then(ObserveException<void>(m_exceptionPolicy));
}

void App::OnResume(Object^ sender, Object^ e)
{
    (void) sender; // Unused parameter
    (void) e;      // Unused parameter

    // NOTE: We pass the app's local settings, which isn't consistent with SuspensionManager, but DirectXPage currently
    // doesn't use the value.
    m_directXPage->LoadInternalState(ApplicationData::Current->LocalSettings->Values);

    Common::SuspensionManager::RestoreAsync().then(ObserveException<void>(m_exceptionPolicy));
}

/// <summary>
/// Invoked when the application is activated to display search results.
/// </summary>
/// <param name="e">Details about the activation request.</param>
void App::OnSearchActivated(SearchActivatedEventArgs^ e)
{
    ActivateLaunch(SearchResultsPage::typeid, e->QueryText, e, true);
}

void App::ActivateLaunch(TypeName navigateTo, Object^ parameter, IActivatedEventArgs^ e, bool navigateToLandingPage)
{
#if _DEBUG
    if (IsDebuggerPresent())
    {
        DebugSettings->EnableFrameRateCounter = true;
    }
#endif

    // If the app does not contain a top-level frame, it is possible that this 
    // is the initial launch of the app.
    if (m_rootFrame == nullptr)
    {
        // Create a Frame to act as the navigation context and associate it with
        // a SuspensionManager key.
        m_rootFrame = ref new Frame();
        Common::SuspensionManager::RegisterFrame(m_rootFrame, "AppFrame");

        m_directXPage = ref new DirectXPage(m_rootFrame);

        auto prerequisite = create_task([]{});
        bool restored = false;
        if (e->PreviousExecutionState == ApplicationExecutionState::Terminated)
        {
            // Restore the saved session state only when appropriate, scheduling the
            // final launch steps after the restore is complete.

            prerequisite = Common::SuspensionManager::RestoreAsync();
            restored = true;
        }
        prerequisite.then([=](task<void> prerequisite)
        {
            try
            {
                prerequisite.get();
            }
            catch (Platform::Exception^)
            {
                // If restore fails, the app should proceed as though there was no restored state.
            }

            if (navigateToLandingPage && !restored)
            {
                // Navigate to the initial landing page of the app as if you were launched. This
                // allows the user to return to your app from the search results page by using the back button.
                m_rootFrame->Navigate(MainPage::typeid, nullptr);
            }

            // If we restored from a previous session, don't navigate to the main page; it's already in the navigation stack.
            if (!restored || navigateTo.Name != TypeName(MainPage::typeid).Name)
            {
                // Navigate to the target page.
                m_rootFrame->Navigate(navigateTo, parameter);
            }

            // Place the frame in the current Window.
            Window::Current->Content = m_directXPage;
            // Ensure the current window is active.
            Window::Current->Activate();

        }, task_continuation_context::use_current());
    }
    else
    {
        // Navigate to the target page.
        m_rootFrame->Navigate(navigateTo, parameter);
        // Ensure the current window is active.
        Window::Current->Activate();
    }
}

/// <summary>
/// Invoked when the application creates a window
/// </summary>
/// <param name="e">Provides the window that was just created.</param>
void App::OnWindowCreated(WindowCreatedEventArgs^ args)
{
    SettingsPane::GetForCurrentView()->CommandsRequested +=
        ref new TypedEventHandler<SettingsPane^, SettingsPaneCommandsRequestedEventArgs^>(&SettingsFlyout::RegisterAppSettingsPane);
}
